

/**
 *
 * @author vimi Administrator
 *  Dinh Ngoc 10.08.2010
 *
 */
import model.*;
import util.*;

import java.util.*;
import java.util.Calendar;

import java.awt.event.*;
import javax.swing.Timer;

import org.apache.log4j.Logger;
import org.apache.log4j.BasicConfigurator;
import org.apache.log4j.Category;
import org.apache.log4j.FileAppender;
import org.apache.log4j.WriterAppender;
import org.apache.log4j.ConsoleAppender;

import org.apache.log4j.PatternLayout;
import org.apache.log4j.PropertyConfigurator;

import org.apache.log4j.Level;

import org.apache.log4j.SimpleLayout;
import java.util.Properties;
import java.io.IOException;

import stats.CustomPlotChart;

import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartFrame;
import org.jfree.chart.JFreeChart;
import org.jfree.data.general.DefaultPieDataset;
import org.jfree.data.xy.DefaultXYDataset;

import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;

import etm.core.configuration.EtmManager;
import etm.core.monitor.EtmMonitor;
import etm.core.monitor.EtmPoint;
import etm.core.configuration.BasicEtmConfigurator;
import etm.core.renderer.SimpleTextRenderer;

import java.io.StringWriter;


public class RegionDigger {
    static final Logger logger = Logger.getLogger(RegionDigger.class.getName());
    // static final String LOG_PROPERTIES_FILE = "config/AppLog4J.properties";

    public InputReader _InputReader;

    public List<Course> Courses;
    private int[] Cs_sortedCs_Mapping=null;
    public List<Room> Rooms;
    private int[] Rs_sortedRs_Mapping=null;
    public int DayNum;
    public int Periods_per_Day;

    public List<Triple> _SolTriples;
    public int [][] Solution;
    public ObjectiveFunction assigned_ObjFunc;

    public int [][] bestFoundSol;
    public ObjectiveFunction assigned_bestObjFunc;

    //public
    public int CH_NCIteration = 3000;
    public int CH_NeighborNum = 50;
    // 
    // 1. select events
    public List<Triple> EventCandidateList;
    /*
    private List<Triple> gen_EventCandidateList;
    private int EventSelect_Probability;
     * 
     */

    // 2. select applicable operator
    private int [] OpIndex = {1,2,3};
    public double Opt1ProbMargin = 0.3333333333;
    public double Opt3ProbMargin = 0.6666666666;


    // 3. list of possible moves für chosen events
    //private ArrayList<Triple> gen_PossibleMoves= new ArrayList<Triple>(); // <Event, <OpType, ArgValues>, ObjectiveFunction>

    // 4. log
    private double sampleIteration= 10;
    private double sampleIterCount= 0;
    private List<XYSeries> ConstraintSerieArray = new ArrayList<XYSeries>();

    public RegionDigger(FirstSolFinder _1stSolFinder)
    {
        this.Courses = _1stSolFinder.CourseDomain;
        this.Cs_sortedCs_Mapping = _1stSolFinder.Cs_sortedCs_Mapping;

        this.Rooms = _1stSolFinder.RoomDomain;
        this.Rs_sortedRs_Mapping = _1stSolFinder.Rs_sortedRs_Mapping;

        this.DayNum = _1stSolFinder._inputReader.DNum;
        this.Periods_per_Day = _1stSolFinder._inputReader.Periods_per_day;

        this._SolTriples = _1stSolFinder.L_R_P_Triples;
        this.Solution = _1stSolFinder.CP_R_Solution;
        //bestFoundSol = new int[Solution.length][Solution[0].length];
        bestFoundSol = Solution.clone();
                //new int[Solution.length][Solution[0].length];

        this._InputReader = _1stSolFinder._inputReader;

        this.assigned_ObjFunc = _1stSolFinder.assigned_ObjFunc;
        this.assigned_bestObjFunc = new ObjectiveFunction(this.assigned_ObjFunc);
        

        this.EventCandidateList = _1stSolFinder.HC4VioRelaxedCandidates;
       
        initializeLogger("C:/RegDiggOut"+this._InputReader.InputFileName+
                "_ClimbHill.log");

        // ContraintSerieArray
        for (int i=1;i< 5;i++)
        {
             XYSeries IthS;

             IthS = new XYSeries("HC"+i);

             ConstraintSerieArray.add(IthS);
        }

        for (int i=1;i< 5;i++)
        {
             XYSeries IthS;

             IthS = new XYSeries("SC"+i);

             ConstraintSerieArray.add(IthS);
        }

        
    }

    public void attachToNewRegionExplorer(NewRegionExplorer _preNewRegionExplorer)
    {
        this._SolTriples =  _preNewRegionExplorer._SolTriples;
        this.Solution =  _preNewRegionExplorer.Solution;

        bestFoundSol =  _preNewRegionExplorer.bestFoundSol;


        this.assigned_ObjFunc =  _preNewRegionExplorer.assigned_ObjFunc;
        this.assigned_bestObjFunc =  _preNewRegionExplorer.assigned_bestObjFunc;


        this.EventCandidateList =  _preNewRegionExplorer.StartEventCandidateList;

    }

    public void attachToRegionDigger(RegionDigger _preRegionDigger)
    {
        this._SolTriples =  _preRegionDigger._SolTriples;
        this.Solution =  _preRegionDigger.Solution;

        bestFoundSol =  _preRegionDigger.bestFoundSol;


        this.assigned_ObjFunc =  _preRegionDigger.assigned_ObjFunc;
        this.assigned_bestObjFunc =  _preRegionDigger.assigned_bestObjFunc;


        this.EventCandidateList =  _preRegionDigger.EventCandidateList;

    }

    private void initializeLogger(String filename)
    {
        PatternLayout layout = new PatternLayout("%m");

         logger.removeAllAppenders();

          FileAppender appender = null;
          try {
             appender = new FileAppender(layout,filename,false);
          } catch(Exception e) {}

        logger.addAppender(appender);
        logger.setLevel((Level) Level.DEBUG);
        logger.addAppender(new ConsoleAppender(layout));
        
    }

    public void run()
    {

         // initialize measurement subsystem
        BasicEtmConfigurator.configure();
        // startup measurement subsystem
        EtmMonitor etmMonitor = EtmManager.getEtmMonitor();
        if (etmMonitor.isStarted()) etmMonitor.reset();
        etmMonitor.start();

        EtmPoint point = etmMonitor.createPoint("Region Digger phase");

        Date StartTime = new Date(); // 1

        climbHill(CH_NCIteration);

        Date EndTime = new Date(); // 2
        long elapsed_time = EndTime.getTime() - StartTime.getTime();
        logger.info("\n  Region Digger phase----}} Time Measurement (ms): "+elapsed_time+"\n");

        // writeSolDown();
        /*
        climbHill(800,true);
        climbHillHardConstraints(800, true);
         */
        point.collect();

        StringWriter writer = new StringWriter();
         //charttest
        SimpleTextRenderer mySimpleTextRenderer= new SimpleTextRenderer();
        etmMonitor.render(mySimpleTextRenderer);
        logger.info(writer.toString());
        etmMonitor.stop();

    }

    public void run2()
    {

         // initialize measurement subsystem
        BasicEtmConfigurator.configure();
        // startup measurement subsystem
        EtmMonitor etmMonitor = EtmManager.getEtmMonitor();
        if (etmMonitor.isStarted()) etmMonitor.reset();
        etmMonitor.start();

        EtmPoint point = etmMonitor.createPoint("Region Digger phase");

        Date StartTime = new Date(); // 1

        // climbHill(CH_NCIteration);
        climbHillHardConstraints(CH_NCIteration, true);

        Date EndTime = new Date(); // 2
        long elapsed_time = EndTime.getTime() - StartTime.getTime();
        logger.info("\n  Region Digger phase----}} Time Measurement (ms): "+elapsed_time+"\n");

        // writeSolDown();
        /*
        climbHill(800,true);
        climbHillHardConstraints(800, true);
         */
        point.collect();

        StringWriter writer = new StringWriter();
         //charttest
        SimpleTextRenderer mySimpleTextRenderer= new SimpleTextRenderer();
        etmMonitor.render(mySimpleTextRenderer);
        logger.info(writer.toString());
        etmMonitor.stop();

    }

    private void climbHill(int NIIteration)
    {
        int minObjFunc = assigned_bestObjFunc.value();
        int NCIteration = 0;
        int Iteration = 0;

        EtmMonitor etmMonitor = EtmManager.getEtmMonitor();
        //etmMonitor.start();
        EtmPoint IterPoint=null;
        Date StartTime = null;

        while (NCIteration<NIIteration)
        {
            Iteration++;
            if (Iteration==1)
            {
                IterPoint= etmMonitor.createPoint("Region Digger phase- Iteration");
                StartTime = new Date(); // 1
            }
            
            logger.info("\n");
            logger.info("climbHill("+NIIteration+") - ######################### Iteration "+Iteration
                                                                           + " NC"+NCIteration+"\n\n");
            logger.info("  EventCandidateList-size:"+this.EventCandidateList.size());
             

            if (this.EventCandidateList !=null)
            {
                logger.info("\n  EventCandidateList-Items: \n");
                for(Triple IthTriple:EventCandidateList)
                {
                    logger.info("                          <"+IthTriple.getFirst()+","+IthTriple.getSecond()+","+IthTriple.getThird()+"> \n");
                }
            }
         
            

            //Triple move =  selectMove(generateNeighborMoves(50),assigned_ObjFunc.value());
            Triple move =  selectMove(generateNeighborMoves(CH_NeighborNum),assigned_bestObjFunc.value());
            if (move != null)
            {
                doMove(move);

                
                logger.info("\n doMove:"+"\n");
                //Interation = 0;

                Object _part1 = move.getFirst();

                if (_part1 instanceof Pair)
                {

                    Triple _Event1 = (Triple) ((Pair) _part1).getFirst();
                    Triple _Event2 = (Triple) ((Pair) _part1).getSecond();


                    logger.info(" + Event1:"+_Event1.getFirst()+","
                                                +_Event1.getSecond()+","+
                                                 _Event1.getThird()+"\n");
                    logger.info(" + Event2:"+_Event2.getFirst()+","
                                                +_Event2.getSecond()+","+
                                                 _Event2.getThird()+"\n");



                }else
                {
                    Triple _Event1 = (Triple) _part1;

                    logger.info("Event1:"+_Event1.getFirst()+","
                                                +_Event1.getSecond()+","+
                                                 _Event1.getThird()+"\n");
                }

                Triple _part2 = (Triple) move.getSecond();

                logger.info(" -- Opt, newRoom, newTimeslot:"+_part2.getFirst()+","
                                                +_part2.getSecond()+","+
                                                 _part2.getThird()+"\n");

                logger.info(" -- OF:"+move.getThird()+"\n");

                

                printPenalties(this.Solution,assigned_ObjFunc);
                
            }

            int afterObjFunc = assigned_ObjFunc.value();
            
            logger.info("     afterObjFunc: "+afterObjFunc+" "+this.assigned_ObjFunc.HC1+"+"+
                                this.assigned_ObjFunc.HC2+"+"+
                                this.assigned_ObjFunc.HC3+"+"+
                                this.assigned_ObjFunc.HC4+" + "+
                                this.assigned_ObjFunc.SC1+"+"+
                                this.assigned_ObjFunc.SC2+"+"+
                                this.assigned_ObjFunc.SC3+"+"+
                                this.assigned_ObjFunc.SC4+"=>"+
                                this.assigned_ObjFunc.value()+"\n"
                                );

            logger.info("     minObjFunc "+minObjFunc+" "+this.assigned_bestObjFunc.HC1+"+"+
                                this.assigned_bestObjFunc.HC2+"+"+
                                this.assigned_bestObjFunc.HC3+"+"+
                                this.assigned_bestObjFunc.HC4+" + "+
                                this.assigned_bestObjFunc.SC1+"+"+
                                this.assigned_bestObjFunc.SC2+"+"+
                                this.assigned_bestObjFunc.SC3+"+"+
                                this.assigned_bestObjFunc.SC4+"=>"+
                                this.assigned_bestObjFunc.value()+"\n"
                                );
           

            logger.info(this.assigned_ObjFunc.value()+"\n");

            if (afterObjFunc>=minObjFunc) NCIteration++;
            else
            {
                minObjFunc = afterObjFunc;
                NCIteration =0;

                bestFoundSol = Solution.clone();

                /*
                for (int c=0;c<bestFoundSol.length;c++)
                {
                    for (int p=0;p<bestFoundSol[0].length;p++)
                    {
                        bestFoundSol[c][p]= Solution[c][p];
                    }
                }
                */
                
                //this.printPenalties(bestFoundSol, assigned_bestObjFunc);

                assigned_bestObjFunc.copyFrom( assigned_ObjFunc) ;

            }



            if (Iteration==1)
            {
                Date EndTime = new Date(); // 2
                long elapsed_time = EndTime.getTime() - StartTime.getTime();
                logger.info("\n  Iteration Digger phase----}} Time Measurement (ms): "+elapsed_time+"\n");
                IterPoint.collect();
            }

            // log convergence
            if (sampleIterCount==sampleIteration)
            {
               this.sampleIterCount=0;

               /*
               for (int i=0;i< ConstraintSerieArray.size();i++)
                {
                    ConstraintSerieArray.get(i).add(NCIteration, assigned_ObjFunc.value());
                }
                * 
                */

               ConstraintSerieArray.get(0).add(Iteration, assigned_ObjFunc.HC1);
               ConstraintSerieArray.get(1).add(Iteration, assigned_ObjFunc.HC2);
               ConstraintSerieArray.get(2).add(Iteration, assigned_ObjFunc.HC3);
               ConstraintSerieArray.get(3).add(Iteration, assigned_ObjFunc.HC4);

               ConstraintSerieArray.get(4).add(Iteration, assigned_ObjFunc.SC1);
               ConstraintSerieArray.get(5).add(Iteration, assigned_ObjFunc.SC2);
               ConstraintSerieArray.get(6).add(Iteration, assigned_ObjFunc.SC3);
               ConstraintSerieArray.get(7).add(Iteration, assigned_ObjFunc.SC4);

            }else sampleIterCount++;


        }


    }


    private void climbHill(int NIIteration, boolean wGreaterOFMove)
    {
        int minObjFunc = assigned_bestObjFunc.value();
        int NCIteration = 0;
        int Iteration = 0;


        while (NCIteration<NIIteration)
        {
            Iteration++;
            logger.info("\n");
            logger.info("climbHill("+NIIteration+","+wGreaterOFMove+") - ######################### Iteration "+Iteration
                                                                           + " NC"+NCIteration+"\n");
            logger.info(" ");

            Triple move=null;

            if (wGreaterOFMove) move =  selectMove(generateNeighborMoves(50),-1);
            else move =  selectMove(generateNeighborMoves(50),assigned_ObjFunc.value());

            if (move != null)
            {
                doMove(move);
                logger.info("doMove:"+"\n");
                //Interation = 0;

                Object _part1 = move.getFirst();

                if (_part1 instanceof Pair)
                {

                    Triple _Event1 = (Triple) ((Pair) _part1).getFirst();
                    Triple _Event2 = (Triple) ((Pair) _part1).getSecond();

                    logger.info(" + Event1:"+_Event1.getFirst()+","
                                                +_Event1.getSecond()+","+
                                                 _Event1.getThird()+"\n");
                    logger.info(" + Event2:"+_Event2.getFirst()+","
                                                +_Event2.getSecond()+","+
                                                 _Event2.getThird()+"\n");


                }else
                {
                    Triple _Event1 = (Triple) _part1;

                    logger.info("Event1:"+_Event1.getFirst()+","
                                                +_Event1.getSecond()+","+
                                                 _Event1.getThird()+"\n");
                }

                Triple _part2 = (Triple) move.getSecond();

                logger.info(" -- Opt, newRoom, newTimeslot:"+_part2.getFirst()+","
                                                +_part2.getSecond()+","+
                                                 _part2.getThird()+"\n");

                logger.info(" -- OF:"+move.getThird()+"\n");

                printPenalties(this.Solution,assigned_ObjFunc);
            }

            int afterObjFunc = assigned_ObjFunc.value();

            logger.info("   afterObjFunc: "+afterObjFunc+" "+ this.assigned_ObjFunc.HC1+"+"+
                                this.assigned_ObjFunc.HC2+"+"+
                                this.assigned_ObjFunc.HC3+"+"+
                                this.assigned_ObjFunc.HC4+" + "+
                                this.assigned_ObjFunc.SC1+"+"+
                                this.assigned_ObjFunc.SC2+"+"+
                                this.assigned_ObjFunc.SC3+"+"+
                                this.assigned_ObjFunc.SC4+"=>"+
                                this.assigned_ObjFunc.value()+"\n"
                                );

            logger.info("   minObjFunc "+minObjFunc+" "+this.assigned_bestObjFunc.HC1+"+"+
                                this.assigned_bestObjFunc.HC2+"+"+
                                this.assigned_bestObjFunc.HC3+"+"+
                                this.assigned_bestObjFunc.HC4+" + "+
                                this.assigned_bestObjFunc.SC1+"+"+
                                this.assigned_bestObjFunc.SC2+"+"+
                                this.assigned_bestObjFunc.SC3+"+"+
                                this.assigned_bestObjFunc.SC4+"=>"+
                                this.assigned_bestObjFunc.value()+"\n"
                                );


            if (afterObjFunc>=minObjFunc) NCIteration++;
            else
            {
                minObjFunc = afterObjFunc;
                NCIteration =0;

                bestFoundSol = Solution.clone();

                /*
                for (int c=0;c<bestFoundSol.length;c++)
                {
                    for (int p=0;p<bestFoundSol[0].length;p++)
                    {
                        bestFoundSol[c][p]= Solution[c][p];
                    }
                }
                 *
                 */

                //assigned_bestObjFunc = assigned_ObjFunc ;


                this.printPenalties(bestFoundSol, assigned_bestObjFunc);

            }


        }


    }

    private void climbHillHardConstraints(int NIIteration, boolean wGreaterOFMove)
    {
        int minObjFunc = assigned_bestObjFunc.valueHCwW();
        int NCIteration = 0;
        int Iteration = 0;


        while (NCIteration<NIIteration)
        {
            Iteration++;
            logger.info("\n");
            logger.info("climbHillHardConstraints - ######################### Iteration "+Iteration
                                                                           + " NC"+NCIteration+"\n");


            Triple move=null;

            if (wGreaterOFMove) move =  selectMove(generateNeighborMoves(50),-1);
            else move =  selectMove(generateNeighborMoves(50),assigned_ObjFunc.valueHCwW());

            if (move != null)
            {
                doMove(move);
                logger.info("doMove:"+"\n");
                //Interation = 0;

                Object _part1 = move.getFirst();

                if (_part1 instanceof Pair)
                {

                    Triple _Event1 = (Triple) ((Pair) _part1).getFirst();
                    Triple _Event2 = (Triple) ((Pair) _part1).getSecond();

                    logger.info(" + Event1:"+_Event1.getFirst()+","
                                                +_Event1.getSecond()+","+
                                                 _Event1.getThird()+"\n");
                    logger.info(" + Event2:"+_Event2.getFirst()+","
                                                +_Event2.getSecond()+","+
                                                 _Event2.getThird()+"\n");


                }else
                {
                    Triple _Event1 = (Triple) _part1;

                    logger.info("Event1:"+_Event1.getFirst()+","
                                                +_Event1.getSecond()+","+
                                                 _Event1.getThird()+"\n");
                }

                Triple _part2 = (Triple) move.getSecond();

                logger.info(" -- Opt, newRoom,newTimeslot:"+_part2.getFirst()+","
                                                +_part2.getSecond()+","+
                                                 _part2.getThird()+"\n");

                logger.info(" -- OF:"+move.getThird()+"\n");

                printPenalties(this.Solution,assigned_ObjFunc);
            }

            int afterObjFunc = assigned_ObjFunc.valueHCwW();

            
            logger.info("   afterObjFunc: "+afterObjFunc+" "+this.assigned_ObjFunc.HC1+"+"+
                                this.assigned_ObjFunc.HC2+"+"+
                                this.assigned_ObjFunc.HC3+"+"+
                                this.assigned_ObjFunc.HC4+" + "+
                                this.assigned_ObjFunc.SC1+"+"+
                                this.assigned_ObjFunc.SC2+"+"+
                                this.assigned_ObjFunc.SC3+"+"+
                                this.assigned_ObjFunc.SC4+"=>"+
                                this.assigned_ObjFunc.value()+"\n"
                                );

            logger.info("   minObjFunc: "+minObjFunc+" "+this.assigned_bestObjFunc.HC1+"+"+
                                this.assigned_bestObjFunc.HC2+"+"+
                                this.assigned_bestObjFunc.HC3+"+"+
                                this.assigned_bestObjFunc.HC4+" + "+
                                this.assigned_bestObjFunc.SC1+"+"+
                                this.assigned_bestObjFunc.SC2+"+"+
                                this.assigned_bestObjFunc.SC3+"+"+
                                this.assigned_bestObjFunc.SC4+"=>"+
                                this.assigned_bestObjFunc.value()+"\n"
                                );


            if (afterObjFunc>=minObjFunc) NCIteration++;
            else
            {
                minObjFunc = afterObjFunc;
                NCIteration =0;

                bestFoundSol = Solution.clone();

                /*
                for (int c=0;c<bestFoundSol.length;c++)
                {
                    for (int p=0;p<bestFoundSol[0].length;p++)
                    {
                        bestFoundSol[c][p]= Solution[c][p];
                    }
                }
                 *
                 */

                // this.printPenalties(bestFoundSol, assigned_bestObjFunc);

                // assigned_bestObjFunc = assigned_ObjFunc ;


                this.printPenalties(bestFoundSol, assigned_bestObjFunc);

            }


        }


    }


    private Triple selectMove(List<Triple> _NeighborMoves, int OF)
    {
         Triple move = _NeighborMoves.get(0);
         int moveOF = Integer.parseInt(move.getThird().toString());

         if (OF>-1)
         {
           if (moveOF<=OF) return move;
         }else if (move!=null) return move;

         return null;
    }

    
    
    private void doMove(Triple move)
    {
        Object Part1=move.getFirst();
        
        Triple Part2= (Triple) move.getSecond();
        int OptType = Integer.parseInt(Part2.getFirst().toString());
        int newRoom = Integer.parseInt(Part2.getSecond().toString());
        int newTimeslot = Integer.parseInt(Part2.getThird().toString());

        int OF_Part3 = Integer.parseInt(move.getThird().toString());


        if (Part1 instanceof Pair)
        {
            if (OptType==3)
            {
                Triple Event1 = (Triple) ((Pair) Part1).getFirst();
                Triple Event2 = (Triple) ((Pair) Part1).getSecond();

                SwitchTime4Events(Event1,Event2, true);
                
            }
        }else
        {
            Triple Event1 = (Triple) Part1;

            if (OptType==1)
            {
                ChangeTime(Event1,newTimeslot,true);
            }

            if (OptType==2)
            {
                ChangeRoom(Event1,newRoom,true);
            }

        }


        
    }
    


    public List<Triple> generateNeighborMoves(int Num)
    {
        List<Triple> retMoves = new ArrayList<Triple>();



        for (int i=0;i<Num;i++)
        {
            Triple IthMove = generateMove();

            if (!retMoves.contains(IthMove))
            {
                int insertPos=0;
                for (Triple move:retMoves)
                {
                    int IthOF= Integer.parseInt(IthMove.getThird().toString());
                    int moveOF= Integer.parseInt(move.getThird().toString());
                    if (IthOF<=moveOF) break;
                    insertPos++;
                }
                retMoves.add(insertPos,IthMove);
            }else i--;

        }




        return retMoves; // sorted
    }


    public Triple generateMove()
    {
        // logger.info("generateMove");

        // 1. Select Event
        Triple selectedEvent= null;

        // 20% - 80%
        while (selectedEvent == null)
        {
            if (Math.random()<0.4)
            {
                selectedEvent = _SolTriples.get(RandomUtil.retRandNum(_SolTriples.size()-1));
            }else
            {
                if (!EventCandidateList.isEmpty())
                {
                selectedEvent = EventCandidateList.get(0);
                }
            }
        }
        

        // 2.1. Select Opt
        int OptTyp = 1; // OpIndex[RandomUtil.retRandNum(2)];
        double randNum = Math.random();

        if (randNum<=Opt1ProbMargin) OptTyp = 1;
        else
        {
            if (randNum>Opt3ProbMargin) OptTyp = 3;
            else OptTyp = 2;
        }


        //logger.info("OptTyp: "+OptTyp);
        

        // 2.2. chooseArgValues for chosen Opt
        // a.
        int c = Integer.parseInt(selectedEvent.getFirst().toString());
        int [] TabuPeriods = new int[Courses.get(c).getLectureNum()];
        int lastP = 0;
        for (int j=0;j<Solution[c].length;j++)
        {

                if (Solution[c][j]>-1)
                {
                    // int r = Solution[c][j];
                    TabuPeriods[lastP] = j;
                    if (lastP<TabuPeriods.length-1) lastP++;
                }
        }

        int newTimeSlot=0;
        

        // b.
        // int [] TabuRooms;
        int newRoom=0;

        boolean HC2Violated = true;
        int HC2ViolatedLoop = 0;
        while (HC2Violated && HC2ViolatedLoop<300)
        {

            newTimeSlot = RandomUtil.retRandNum(Solution[0].length-1,TabuPeriods);
            newRoom = RandomUtil.retRandNum(Rooms.size()-1,Integer.parseInt(selectedEvent.getSecond().toString()));

            HC2Violated = testHC2Violation(c,newRoom, newTimeSlot,this.Solution);
            HC2ViolatedLoop++;
        }

        // c.
        Triple selectedEvent2 = null;
        if (OptTyp==3)
        {
            int selEvent2Loop = 0;
            while (selectedEvent2 == null || selectedEvent2==selectedEvent)
            {


                if (Math.random()<0.2)
                {
                    selectedEvent2 = _SolTriples.get(RandomUtil.retRandNum(_SolTriples.size()-1));
                }else
                {
                  if (!EventCandidateList.isEmpty())
                  {
                    selectedEvent2 = EventCandidateList.get(0);
                  }
                }

                // selectedEvent2 must not have the same timeslot with a lecture of the course in selectedEvent
                if (selectedEvent2 !=null && selEvent2Loop < 3000)
                {
                    int _p=Integer.parseInt(selectedEvent2.getThird().toString());;
                    if (Solution[c][_p]>-1) selectedEvent2= null;
                    selEvent2Loop++;
                }

            }
        }

        // 3. precalc ObjectiveFunction
        int [][] _tempSolution = null;


        if (OptTyp ==1)
        {
            _tempSolution=ChangeTime(selectedEvent, newTimeSlot, false);
        }

        if (OptTyp ==2)
        {
            _tempSolution= ChangeRoom(selectedEvent, newRoom, false);
        }

        if (OptTyp ==3)
        {
            if (selectedEvent2 !=null)
            {
            _tempSolution=SwitchTime4Events(selectedEvent, selectedEvent2, false);
            EventCandidateList.remove(selectedEvent2);
            }else _tempSolution = Solution;
        }

        int preHCValue = this.assigned_ObjFunc.valueHCwW();

        printPenalties(_tempSolution, this.assigned_ObjFunc);

        if (this.EventCandidateList.size()<100)
        {
          if (this.assigned_ObjFunc.valueHCwW()>preHCValue+10)
          {
              if (!testContain((ArrayList) EventCandidateList,selectedEvent)) this.EventCandidateList.add(selectedEvent);
          }
        }


        int ObjectiveFunc = this.assigned_ObjFunc.value();

        // 4. build data parts of Triple
        Object Part1;

        if (selectedEvent2!= null)
        {
            Part1 = new Pair(selectedEvent,selectedEvent2);
        }else Part1 = selectedEvent;

        Triple Part2= new Triple(OptTyp,newRoom,newTimeSlot);

        int Part3 = ObjectiveFunc;

        return new Triple(Part1,Part2,Part3);
    }

    private boolean testContain(ArrayList<Triple> _EventCandidateList, Triple _selectedEvent)
    {
        
        for(int i=0;i<_EventCandidateList.size();i++)
        {
            Triple IthEvent = _EventCandidateList.get(i);
            if (IthEvent.getFirst().toString().equals(_selectedEvent.getFirst().toString())
                && IthEvent.getSecond().toString().equals(_selectedEvent.getSecond().toString())
                && IthEvent.getThird().toString().equals(_selectedEvent.getThird().toString())
               ) return true;
        }

        return false;
    }

    private boolean testHC2Violation(int _c,int _Room, int _TimeSlot, int [][] _Solution)
    {
        for (int c=0;c<_Solution.length;c++)
        {
            if (c != _c && _Solution[c][_TimeSlot]==_Room)
            {
                return true;
            }
        }

        return false;
    }

    private void printPenalties(int [][] _sol, ObjectiveFunction _ObFunc)
    {
        PernaltyCounter _PenaltyCounter = new PernaltyCounter();
        this.attachTo(_PenaltyCounter);
        _PenaltyCounter.CP_R_Solution = _sol;

        if (_ObFunc==null)
        {
            _ObFunc = new ObjectiveFunction();
        }

        _ObFunc.assigned_Solution = _sol;

        _ObFunc.HC1 = _PenaltyCounter.AllLecturesOnTT();
        _ObFunc.HC2 = _PenaltyCounter.RoomOccupancy();
        _ObFunc.HC3 = _PenaltyCounter.sameCurriculumLecturesConflict(_InputReader.HardCurriculum_Matrix, this.Cs_sortedCs_Mapping)
                         +_PenaltyCounter.sameTeacherLecturesConflict(_InputReader.Courses_waTeacher_Matrix, this.Cs_sortedCs_Mapping);
        _ObFunc.HC4 = _PenaltyCounter.TeacherAvailabilities(_InputReader.C_P_UnavailabilityMatrix, this.Cs_sortedCs_Mapping);

         _ObFunc.SC1 = _PenaltyCounter.RoomCapacity();
         _ObFunc.SC2 = _PenaltyCounter.MinimumWorkingDays(_InputReader.Periods_per_day);
         _ObFunc.SC3 = _PenaltyCounter.CurriculumCompactness();
         _ObFunc.SC4 = _PenaltyCounter.RoomStability();


    }

    public void attachTo(PernaltyCounter _pc)
    {
        _pc.CourseDomain = this.Courses; 
        _pc.Curricula = this._InputReader.Curricula;
        _pc.DayNum = this.DayNum;
        _pc.periods_per_day = this.Periods_per_Day;

        _pc.RoomDomain = this.Rooms;
        _pc.CP_R_Solution = this.Solution;

        /*
        Curriculum IthCurriculum = _pc.Curricula.get(0);

        logger.info(IthCurriculum.CourseIDs.get(0));
        logger.info(IthCurriculum.CIndexList.get(0));
         *
         */

        //_pc.printSolMatrix();


    }


    private int [][] ChangeTime(Triple lecture, int newTimeSlot,boolean notTest)
    {
       int [][] _tempSolution = new int[this.Solution.length][this.Solution[0].length];



        for (int i=0;i<_tempSolution.length;i++)
        {
            for (int j=0;j<_tempSolution[i].length;j++)
            {
                _tempSolution[i][j]= Solution[i][j];
            }
        }

       int c= Integer.parseInt(lecture.getFirst().toString());
       int r= Integer.parseInt(lecture.getSecond().toString());
       int p= Integer.parseInt(lecture.getThird().toString());

       _tempSolution[c][p] =-1;
       _tempSolution[c][newTimeSlot] =r;

       if (notTest)
       {
           Solution[c][p] =-1;
           Solution[c][newTimeSlot] =r;
           lecture.setThird(newTimeSlot);
           //if (notTest) logger.info("ChangeTime");
       }

        return _tempSolution;
    }

    private int [][] ChangeRoom(Triple lecture, int newRoom, boolean notTest)
    {
        int [][] _tempSolution = new int[this.Solution.length][this.Solution[0].length];

        for (int i=0;i<_tempSolution.length;i++)
        {
            for (int j=0;j<_tempSolution[i].length;j++)
            {
                _tempSolution[i][j]= Solution[i][j];
            }
        }

       int c= Integer.parseInt(lecture.getFirst().toString());
       int r= Integer.parseInt(lecture.getSecond().toString());
       int p= Integer.parseInt(lecture.getThird().toString());

       _tempSolution[c][p] =newRoom;

       if (notTest)
       {
           Solution[c][p] = newRoom;
           lecture.setSecond(newRoom);

           //if (notTest) logger.info("ChangeRoom");

       }
       
        return _tempSolution;
        
    }

    private int [][] SwitchTime4Events(Triple lecture1, Triple lecture2, boolean notTest)
    {
        int [][] _tempSolution = new int[this.Solution.length][this.Solution[0].length];

        for (int i=0;i<_tempSolution.length;i++)
        {
            for (int j=0;j<_tempSolution[i].length;j++)
            {
                _tempSolution[i][j]= Solution[i][j];
            }
        }

       int c1= Integer.parseInt(lecture1.getFirst().toString());
       int r1= Integer.parseInt(lecture1.getSecond().toString());
       int p1= Integer.parseInt(lecture1.getThird().toString());

       int c2= Integer.parseInt(lecture2.getFirst().toString());
       int r2= Integer.parseInt(lecture2.getSecond().toString());
       int p2= Integer.parseInt(lecture2.getThird().toString());

       _tempSolution[c1][p1] =-1;
       _tempSolution[c1][p2] =r2;

       _tempSolution[c2][p2] =-1;
       _tempSolution[c2][p1] =r1;

       if (notTest)
       {
           Solution[c1][p1] =-1;
           Solution[c1][p2] =r2; // r1

           Solution[c2][p2] =-1;
           Solution[c2][p1] =r1;
           lecture1.setThird(p2);
           lecture2.setThird(p1);

           //if (notTest) logger.info("SwitchTime");
           
       }

        return _tempSolution;
        
    }

    public void writeSolDown()
    {
        SolWriter _solWriter = new SolWriter("C:/RegDiggOut"+this._InputReader.InputFileName+
                "_CHSol.sol");

        // Calendar.getInstance().getTime()
        _solWriter.Courses = this.Courses;
        _solWriter.Rooms = this.Rooms;
        _solWriter.Solution = this.bestFoundSol;

        _solWriter.DayNum = this.DayNum;
        _solWriter.Periods_per_Day = this.Periods_per_Day;

        _solWriter.writeSolDown();

    }


    protected void finalize()
    {
        writeSolDown();
    }


    public void showBestSolinChart()
    {

        // A scatter plot displays data from an XYDataset.
        
        // create a dataset...
        List<XYSeries> SerieArray = new ArrayList<XYSeries>();

         for (int i=0;i< this.Courses.size();i++)
         {
             XYSeries IthS = new XYSeries(Courses.get(i).getCourseID());

             SerieArray.add(IthS);
         }


        for (int i=0;i< this.bestFoundSol.length;i++)
        {
            //System.out.print(myInputReader.Courses.get(i).getCourseID()+": ");

            for (int j=0;j< this.bestFoundSol[i].length;j++)
            {
                //System.out.print(myInputReader.HardCurriculum_Matrix[i][j]+" ");
                if (this.bestFoundSol[i][j]>-1) SerieArray.get(i).add(j, this.bestFoundSol[i][j]);
            }

        }




        XYSeriesCollection dataset = new XYSeriesCollection();

        for (int i=0;i< SerieArray.size();i++)
         {
             XYSeries IthS = SerieArray.get(i);

             dataset.addSeries(IthS);
         }

        // create a chart...

        CustomPlotChart chartFrame = new CustomPlotChart("Phase Region Digger - Solution "+this._InputReader.InputFileName,"Period("+_InputReader.DNum+"x"+_InputReader.Periods_per_day+")"
                   +"         OF:"+this.assigned_bestObjFunc.HC1+"+"+
                                this.assigned_bestObjFunc.HC2+"+"+
                                this.assigned_bestObjFunc.HC3+"+"+
                                this.assigned_bestObjFunc.HC4+" + "+
                                this.assigned_bestObjFunc.SC1+"+"+
                                this.assigned_bestObjFunc.SC2+"+"+
                                this.assigned_bestObjFunc.SC3+"+"+
                                this.assigned_bestObjFunc.SC4+"=>"+
                                this.assigned_bestObjFunc.value(),"Room",dataset,"C:/RegDiggOut"+this._InputReader.InputFileName+"_DiggSol.png");
        /*
        chartFrame.setChartText(this.assigned_ObjFunc.HC1+"+"+
                                this.assigned_ObjFunc.HC2+"+"+
                                this.assigned_ObjFunc.HC3+"+"+
                                this.assigned_ObjFunc.HC4+" + "+
                                this.assigned_ObjFunc.SC1+"+"+
                                this.assigned_ObjFunc.SC2+"+"+
                                this.assigned_ObjFunc.SC3+"+"+
                                this.assigned_ObjFunc.SC4+"=>"+
                                this.assigned_ObjFunc.value()
                                );
         *
         */

        chartFrame.setChartText(this.assigned_bestObjFunc.HC1+"+"+
                                this.assigned_bestObjFunc.HC2+"+"+
                                this.assigned_bestObjFunc.HC3+"+"+
                                this.assigned_bestObjFunc.HC4+" + "+
                                this.assigned_bestObjFunc.SC1+"+"+
                                this.assigned_bestObjFunc.SC2+"+"+
                                this.assigned_bestObjFunc.SC3+"+"+
                                this.assigned_bestObjFunc.SC4+"=>"+
                                this.assigned_bestObjFunc.value()
                                );


	chartFrame.pack();
	chartFrame.setVisible(true);

        if (!chartFrame.isShowing())
        {
            
                
              // chartFrame.saveChartAsPNG("C:/RegDiggOut"+this._InputReader.InputFileName+"_DiggSol.png");
          
        }


    }



    public void showConvergenceinChart()
    {

        // A scatter plot displays data from an XYDataset.

        // create a dataset...
       


        XYSeriesCollection dataset = new XYSeriesCollection();

        for (int i=0;i< ConstraintSerieArray.size();i++)
         {
             XYSeries IthS = ConstraintSerieArray.get(i);

             dataset.addSeries(IthS);
         }

        // create a chart...

        CustomPlotChart chartFrame = new CustomPlotChart("Phase Region Digger - Convergence "+this._InputReader.InputFileName,"Iteration","Violation",dataset,"C:/RegDiggOut"+this._InputReader.InputFileName+"_DiggConvergence.png");
       

	chartFrame.pack();
	chartFrame.setVisible(true);

   

    }

}
